Skip to content

feat(security): npm bootstrap path, exact npm pin, and release rulesets - #9

Merged
Radiergummi merged 1 commit into
mainfrom
chore/pin-npm-exactly
Jul 30, 2026
Merged

feat(security): npm bootstrap path, exact npm pin, and release rulesets#9
Radiergummi merged 1 commit into
mainfrom
chore/pin-npm-exactly

Conversation

@Radiergummi

@Radiergummi Radiergummi commented Jul 30, 2026

Copy link
Copy Markdown
Member

Three related changes to the release-security posture.

npm's first publish needs a token — made single-use by construction

npm cannot attach a trusted publisher to a package that does not exist, and there is no way to reserve a name first. So the first publish must authenticate with a token.

The risk isn't the bootstrap, it's the credential quietly becoming permanent. The workflow now refuses the token path once the package exists on npmjs — a release fails while NPM_TOKEN is still present. Cleanup stops being something to remember.

All four states are explicit and were tested:

State Behaviour
token + package absent publish, with a loud warning
token + package exists refuse, tell the operator to delete the secret
no token + absent refuse, explain why a token is needed
no token + exists OIDC — the steady state

Provenance is unaffected, which is the part worth knowing: --provenance derives from the job's Sigstore identity via id-token, not from how npm authenticates. v0.1.0 will be fully attested despite predating trusted publishing on the package.

Also dropped registry-url from setup-node. It writes //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} into .npmrc, which resolves to an empty token when no secret is set — that would have made npm attempt token auth instead of falling back to OIDC, breaking every release after the first.

Exact npm pin

Scorecard scored Pinned-Dependencies 9/10 because release.yml installed npm@11, a floating major. Now pinned to an exact patch; Renovate keeps it current.

Two new rulesets

  • Release integritymain requires the CI checks to pass and signed commits; no deletion, no force-push.
  • Immutable release tagsrefs/tags/v* cannot be deleted, updated, force-pushed, or created unsigned.

The tag ruleset matters because GitHub's immutable-releases setting is not exposed through the REST API. This covers the half that counts: a published version tag cannot be repointed at different code after the fact.


Also the first PR in this repository on purpose — Scorecard scored Code-Review 0 because everything so far landed directly on main, and the new ruleset enforces the better path from here.

SECURITY.md documents the bootstrap as a four-step procedure, with the token deliberately weak: granular, @matchory-scoped, 7-day expiry, stored as a release environment secret rather than a repository one so the existing approval and v*-tag gates already apply to it.

Two gaps OpenSSF Scorecard flagged on the first run (7/10 overall).

Pinned-Dependencies scored 9 rather than 10 because release.yml
installed npm@11, a floating major. Pinned to an exact patch; Renovate
keeps it current.

Added two repository rulesets:

  - Release integrity: main requires the CI checks to pass, signed
    commits, and blocks deletion and force-push.
  - Immutable release tags: refs/tags/v* cannot be deleted, updated,
    force-pushed or created unsigned. GitHub's immutable-releases setting
    is not exposed through the REST API, and this covers the half that
    matters -- a published version tag cannot be repointed at different
    code.

This change deliberately goes through a pull request rather than a direct
push. Scorecard scored Code-Review 0 because every commit so far landed
straight on main, and the new ruleset now enforces the better path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEdTd43qLEEE5qCsL1A7gW
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Radiergummi
Radiergummi merged commit cd64e21 into main Jul 30, 2026
12 checks passed
@Radiergummi Radiergummi changed the title chore(security): pin npm exactly and make release tags immutable feat(security): npm bootstrap path, exact npm pin, and release rulesets Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant